import java.io.*;
import java.util.ArrayList;
import java.util.List;

public class Lex_Main {
    // -------------------------- 核心数据定义 --------------------------
    private static final String[] RESERVE_WORDS = {
            "auto", "break", "case", "char", "const", "continue",
            "default", "do", "double", "else", "enum", "extern",
            "float", "for", "goto", "if", "int", "long",
            "register", "return", "short", "signed", "sizeof", "static",
            "struct", "switch", "typedef", "union", "unsigned", "void",
            "volatile", "while"
    };

    private static final String[] OP_DELIMITERS = {
            "+", "-", "*", "/", "<", "<=", ">", ">=", "=", "==",
            "!=", ";", "(", ")", "^", ",", "\"", "'", "#", "&",
            "&&", "|", "||", "%", "~", "<<", ">>", "[", "]", "{",
            "}", "\\", ".", "?", ":", "!", "+=", "-=", "*=", "/=", "|=", "&="
    };

    private static final int TOKEN_EOF = 0;
    private static final int TOKEN_IDENTIFIER = 100;
    private static final int TOKEN_DEC_INT = 99;
    private static final int TOKEN_FLOAT = 101;
    private static final int TOKEN_STRING = 102;
    private static final int TOKEN_OCT_INT = 103;
    private static final int TOKEN_HEX_INT = 104;
    private static final int TOKEN_PREPROCESSOR = 105;

    private List<String> identifierTable = new ArrayList<>();
    private String filteredSource;
    private int currentPos;
    private int currentLine;
    private int currentCol;
    private BufferedWriter outWriter;

    // -------------------------- 初始化与预处理 --------------------------
    public Lex_Main(String sourcePath, String outputPath) throws IOException {
        File outputFile = new File(outputPath);
        File parentDir = outputFile.getParentFile();
        if (parentDir != null && !parentDir.exists()) {
            parentDir.mkdirs();
        }
        outWriter = new BufferedWriter(new FileWriter(outputFile));

        String originalSource = readSourceWithLineNum(sourcePath);
        filterSource(originalSource);
        currentPos = 0;
        currentLine = 1;
        currentCol = 1;
    }

    private String readSourceWithLineNum(String sourcePath) throws IOException {
        BufferedReader inReader = new BufferedReader(new FileReader(sourcePath));
        StringBuilder originalSb = new StringBuilder();
        String line;
        int lineNum = 1;

        System.out.println("=== 源程序（带行号）===");
        outWriter.write("=== 源程序（带行号）===\n");

        while ((line = inReader.readLine()) != null) {
            String lineWithNum = lineNum + " " + line + "\n";
            System.out.print(lineWithNum);
            outWriter.write(lineWithNum);
            originalSb.append(line).append("\n");
            lineNum++;
        }
        inReader.close();
        System.out.println();
        outWriter.write("\n");
        return originalSb.toString();
    }

    private void filterSource(String originalSource) throws IOException {
        StringBuilder filteredSb = new StringBuilder();
        int state = 0;
        int originalLine = 1;
        int commentStartLine = 1;

        for (int i = 0; i < originalSource.length(); i++) {
            char c = originalSource.charAt(i);
            switch (state) {
                case 0:
                    if (c == '/' && i + 1 < originalSource.length()) {
                        if (originalSource.charAt(i + 1) == '/') {
                            state = 1;
                            i++;
                        } else if (originalSource.charAt(i + 1) == '*') {
                            state = 2;
                            commentStartLine = originalLine;
                            i++;
                        } else {
                            filteredSb.append(c);
                        }
                    } else if (c == '\t' || c == '\r') {
                        continue;
                    } else {
                        if (c == '\n') originalLine++;
                        filteredSb.append(c);
                    }
                    break;

                case 1:
                    if (c == '\n') {
                        state = 0;
                        filteredSb.append(c);
                        originalLine++;
                    }
                    break;

                case 2:
                    if (c == '*' && i + 1 < originalSource.length() && originalSource.charAt(i + 1) == '/') {
                        state = 0;
                        i++;
                    } else if (c == '\n') {
                        originalLine++;
                    }
                    break;
            }
        }

        if (state == 2) {
            String errMsg = "错误：第" + commentStartLine + "行，块注释未闭合（缺少*/）\n";
            System.err.print(errMsg);
            outWriter.write(errMsg);
        }

        filteredSource = filteredSb.toString();
        System.out.println("=== 过滤后程序（去除注释与空白符）===");
        System.out.println(filteredSource);
        outWriter.write("=== 过滤后程序（去除注释与空白符）===\n");
        outWriter.write(filteredSource + "\n");
    }

    // -------------------------- 核心分析方法（修复版） --------------------------
    private int isReserveWord(String token) {
        for (int i = 0; i < RESERVE_WORDS.length; i++) {
            if (RESERVE_WORDS[i].equals(token)) {
                return i + 1;
            }
        }
        return -1;
    }

    private int identifyConstantType(String token) {
        int len = token.length();
        if (len == 0) return -1;

        // 1. 十六进制（0x/0X开头）
        if (len >= 3 && (token.startsWith("0x") || token.startsWith("0X"))) {
            // 必须至少有一个十六进制数字
            if (len == 2) return -1;
            for (int i = 2; i < len; i++) {
                if (!isHexDigit(token.charAt(i))) {
                    return -1; // 含非十六进制字符，非法
                }
            }
            return TOKEN_HEX_INT;
        }

        // 2. 八进制（0开头，后续0-7）
        if (len >= 2 && token.charAt(0) == '0') {
            for (int i = 1; i < len; i++) {
                if (token.charAt(i) < '0' || token.charAt(i) > '7') {
                    return -1; // 含非八进制字符，非法
                }
            }
            return TOKEN_OCT_INT;
        }

        // 3. 浮点数（含.或e/E，无x/X）
        boolean hasDot = false;
        boolean hasE = false;
        for (int i = 0; i < len; i++) {
            char c = token.charAt(i);
            if (c == 'x' || c == 'X') return -1;
            else if (c == '.') {
                if (hasDot || hasE || i == 0 || i == len - 1) return -1;
                hasDot = true;
            } else if (c == 'e' || c == 'E') {
                if (hasE || i == 0 || i == len - 1) return -1;
                hasE = true;
                if (i + 1 < len && (token.charAt(i + 1) == '+' || token.charAt(i + 1) == '-')) {
                    if (i + 2 >= len) return -1;
                    i++;
                }
            } else if (!isDigit(c)) {
                return -1;
            }
        }
        if (hasDot || hasE) return TOKEN_FLOAT;

        // 4. 十进制整数（纯数字，不能以0开头，除非是单个0）
        if (len == 1 && token.charAt(0) == '0') {
            return TOKEN_DEC_INT;
        }
        if (len >= 2 && token.charAt(0) == '0') {
            return -1; // 以0开头的多位数是八进制，应该在前面处理
        }
        for (int i = 0; i < len; i++) {
            if (!isDigit(token.charAt(i))) return -1;
        }
        return TOKEN_DEC_INT;
    }

    public int getToken(StringBuilder token) throws IOException {
        token.setLength(0);
        // 过滤空格
        while (currentPos < filteredSource.length() && filteredSource.charAt(currentPos) == ' ') {
            currentPos++;
            currentCol++;
        }

        if (currentPos >= filteredSource.length()) {
            return TOKEN_EOF;
        }

        char currentChar = filteredSource.charAt(currentPos);

        // 0. 预处理指令（以#开头，直到行尾）
        if (currentChar == '#') {
            token.append(currentChar);
            currentPos++;
            currentCol++;

            while (currentPos < filteredSource.length() && filteredSource.charAt(currentPos) != '\n') {
                token.append(filteredSource.charAt(currentPos));
                currentPos++;
                currentCol++;
            }
            return TOKEN_PREPROCESSOR;
        }

        // 1. 标识符/保留字
        if (isLetter(currentChar)) {
            while (currentPos < filteredSource.length()) {
                char c = filteredSource.charAt(currentPos);
                if (isLetter(c) || isDigit(c)) {
                    token.append(c);
                    currentPos++;
                    currentCol++;
                } else {
                    break;
                }
            }
            int reserveCode = isReserveWord(token.toString());
            if (reserveCode != -1) {
                return reserveCode;
            } else {
                addToIdentifierTable(token.toString());
                return TOKEN_IDENTIFIER;
            }
        }

        // 2. 字符串常量
        else if (currentChar == '"') {
            token.append(currentChar);
            currentPos++;
            currentCol++;
            while (currentPos < filteredSource.length() && filteredSource.charAt(currentPos) != '"') {
                token.append(filteredSource.charAt(currentPos));
                currentPos++;
                currentCol++;
            }
            if (currentPos < filteredSource.length() && filteredSource.charAt(currentPos) == '"') {
                token.append('"');
                currentPos++;
                currentCol++;
                return TOKEN_STRING;
            } else {
                String errMsg = "错误：第" + currentLine + "行第" + (currentCol - token.length()) + "列，字符串未闭合：" + token + "\n";
                System.err.print(errMsg);
                outWriter.write(errMsg);
                return -2;
            }
        }

        // 3. 数字开头的常数
        else if (isDigit(currentChar)) {
            token.append(currentChar);
            currentPos++;
            currentCol++;

            // 检查是否为十六进制
            boolean isHex = false;
            if (token.toString().equals("0") && currentPos < filteredSource.length()) {
                char nextChar = filteredSource.charAt(currentPos);
                if (nextChar == 'x' || nextChar == 'X') {
                    isHex = true;
                    token.append(nextChar);
                    currentPos++;
                    currentCol++;
                }
            }

            if (isHex) {
                // 收集完整的十六进制部分
                boolean hasInvalidHex = false;
                while (currentPos < filteredSource.length()) {
                    char c = filteredSource.charAt(currentPos);
                    if (isHexDigit(c)) {
                        token.append(c);
                        currentPos++;
                        currentCol++;
                    } else if (isLetter(c)) {
                        // 非法十六进制字符（g-z）
                        token.append(c);
                        currentPos++;
                        currentCol++;
                        hasInvalidHex = true;
                    } else {
                        break;
                    }
                }
                if (hasInvalidHex) {
                    String errMsg = "错误：第" + currentLine + "行第" + (currentCol - token.length()) + "列，非法十六进制常数：" + token + "\n";
                    System.err.print(errMsg);
                    outWriter.write(errMsg);
                    return -2;
                }
                return TOKEN_HEX_INT;
            }

            // 非十六进制路径（可能为八进制、十进制或浮点）
            while (currentPos < filteredSource.length()) {
                char c = filteredSource.charAt(currentPos);
                if (isDigit(c) || c == '.' || c == 'e' || c == 'E' ||
                        (c == '+' && token.length() > 0 &&
                                (token.charAt(token.length() - 1) == 'e' || token.charAt(token.length() - 1) == 'E')) ||
                        (c == '-' && token.length() > 0 &&
                                (token.charAt(token.length() - 1) == 'e' || token.charAt(token.length() - 1) == 'E'))) {
                    token.append(c);
                    currentPos++;
                    currentCol++;
                } else {
                    break;
                }
            }

            int constCode = identifyConstantType(token.toString());
            if (constCode == -1) {
                String errMsg = "错误：第" + currentLine + "行第" + (currentCol - token.length()) + "列，非法常数：" + token + "\n";
                System.err.print(errMsg);
                outWriter.write(errMsg);
                return -2;
            }
            return constCode;
        }

        // 4. .开头的浮点数
        else if (currentChar == '.' && currentPos + 1 < filteredSource.length() && isDigit(filteredSource.charAt(currentPos + 1))) {
            token.append(currentChar);
            currentPos++;
            currentCol++;

            while (currentPos < filteredSource.length()) {
                char c = filteredSource.charAt(currentPos);
                if (isDigit(c) || c == 'e' || c == 'E' ||
                        (c == '+' && token.length() > 0 &&
                                (token.charAt(token.length() - 1) == 'e' || token.charAt(token.length() - 1) == 'E')) ||
                        (c == '-' && token.length() > 0 &&
                                (token.charAt(token.length() - 1) == 'e' || token.charAt(token.length() - 1) == 'E'))) {
                    token.append(c);
                    currentPos++;
                    currentCol++;
                } else {
                    break;
                }
            }

            int constCode = identifyConstantType(token.toString());
            if (constCode == -1) {
                String errMsg = "错误：第" + currentLine + "行第" + (currentCol - token.length()) + "列，非法常数：" + token + "\n";
                System.err.print(errMsg);
                outWriter.write(errMsg);
                return -2;
            }
            return constCode;
        }

        // 5. 复合赋值、++, -- 等运算符
        else if (currentChar == '+' || currentChar == '-' || currentChar == '*' || currentChar == '/' || currentChar == '|' || currentChar == '&') {
            token.append(currentChar);
            currentPos++;
            currentCol++;
            if (currentPos < filteredSource.length() && filteredSource.charAt(currentPos) == '=') {
                token.append('=');
                currentPos++;
                currentCol++;
            } else if ((currentChar == '+' || currentChar == '-') &&
                    currentPos < filteredSource.length() && filteredSource.charAt(currentPos) == currentChar) {
                token.append(currentChar);
                currentPos++;
                currentCol++;
            }
            for (int i = 0; i < OP_DELIMITERS.length; i++) {
                if (OP_DELIMITERS[i].equals(token.toString())) {
                    return 33 + i;
                }
            }
        }

        // 6. 多字符运算符（<, >, =, !, &&, || 等）
        else if (currentChar == '<') {
            token.append(currentChar);
            currentPos++;
            currentCol++;
            if (currentPos < filteredSource.length() && filteredSource.charAt(currentPos) == '=') {
                token.append('=');
                currentPos++;
                currentCol++;
                return 38;
            } else if (currentPos < filteredSource.length() && filteredSource.charAt(currentPos) == '<') {
                token.append('<');
                currentPos++;
                currentCol++;
                return 58;
            } else {
                return 37;
            }
        } else if (currentChar == '>') {
            token.append(currentChar);
            currentPos++;
            currentCol++;
            if (currentPos < filteredSource.length() && filteredSource.charAt(currentPos) == '=') {
                token.append('=');
                currentPos++;
                currentCol++;
                return 40;
            } else if (currentPos < filteredSource.length() && filteredSource.charAt(currentPos) == '>') {
                token.append('>');
                currentPos++;
                currentCol++;
                return 59;
            } else {
                return 39;
            }
        } else if (currentChar == '=') {
            token.append(currentChar);
            currentPos++;
            currentCol++;
            if (currentPos < filteredSource.length() && filteredSource.charAt(currentPos) == '=') {
                token.append('=');
                currentPos++;
                currentCol++;
                return 42;
            } else {
                return 41;
            }
        } else if (currentChar == '!') {
            token.append(currentChar);
            currentPos++;
            currentCol++;
            if (currentPos < filteredSource.length() && filteredSource.charAt(currentPos) == '=') {
                token.append('=');
                currentPos++;
                currentCol++;
                return 43;
            } else {
                return 68;
            }
        } else if (currentChar == '&') {
            token.append(currentChar);
            currentPos++;
            currentCol++;
            if (currentPos < filteredSource.length() && filteredSource.charAt(currentPos) == '&') {
                token.append('&');
                currentPos++;
                currentCol++;
                return 53;
            } else {
                return 52;
            }
        } else if (currentChar == '|') {
            token.append(currentChar);
            currentPos++;
            currentCol++;
            if (currentPos < filteredSource.length() && filteredSource.charAt(currentPos) == '|') {
                token.append('|');
                currentPos++;
                currentCol++;
                return 55;
            } else {
                return 54;
            }
        }

        // 7. 单字符运算符/界符
        else if (isOpDelimiter(currentChar)) {
            token.append(currentChar);
            currentPos++;
            currentCol++;
            for (int i = 0; i < OP_DELIMITERS.length; i++) {
                if (OP_DELIMITERS[i].equals(token.toString())) {
                    return 33 + i;
                }
            }
        }

        // 8. 换行符
        else if (currentChar == '\n') {
            currentLine++;
            currentCol = 1;
            currentPos++;
            return getToken(token);
        }

        // 9. 非法字符
        else {
            String errMsg = "错误：第" + currentLine + "行第" + currentCol + "列，非法字符：" + currentChar + "\n";
            System.err.print(errMsg);
            outWriter.write(errMsg);
            currentPos++;
            currentCol++;
            return -2;
        }

        return -2;
    }




    // -------------------------- 辅助方法 --------------------------
    private boolean isLetter(char c) {
        return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
    }

    private boolean isDigit(char c) {
        return c >= '0' && c <= '9';
    }

    private boolean isHexDigit(char c) {
        return isDigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
    }

    private void addToIdentifierTable(String token) {
        if (!identifierTable.contains(token)) {
            identifierTable.add(token);
        }
    }

    private boolean isOpDelimiter(char c) {
        for (String op : OP_DELIMITERS) {
            if (op.length() == 1 && op.charAt(0) == c) {
                return true;
            }
        }
        return false;
    }

    private String getTypeName(int syn) {
        if (syn >= 1 && syn <= 32) return "保留字";
        else if (syn >= 33 && syn <= OP_DELIMITERS.length + 32) return "运算符/界符";
        else if (syn == TOKEN_IDENTIFIER) return "标识符";
        else if (syn == TOKEN_DEC_INT) return "十进制整数";
        else if (syn == TOKEN_OCT_INT) return "八进制整数";
        else if (syn == TOKEN_HEX_INT) return "十六进制整数";
        else if (syn == TOKEN_FLOAT) return "浮点数";
        else if (syn == TOKEN_STRING) return "字符串常量";
        else if (syn == TOKEN_PREPROCESSOR) return "预处理指令";
        else return "未知类型";
    }

    private void outputIdentifierTable() throws IOException {
        System.out.println("\n=== 标识符表 ===");
        outWriter.write("\n=== 标识符表 ===\n");
        for (int i = 0; i < identifierTable.size(); i++) {
            String line = "第" + (i + 1) + "个标识符：" + identifierTable.get(i) + "\n";
            System.out.print(line);
            outWriter.write(line);
        }
    }

    public void startAnalysis() throws IOException {
        StringBuilder token = new StringBuilder();
        int syn;

        System.out.println("=== Token 分析结果（二元式）===");
        outWriter.write("=== Token 分析结果（二元式）===\n");

        while ((syn = getToken(token)) != TOKEN_EOF) {
            if (syn == -2) continue;

            String typeName = getTypeName(syn);
            String result;
            if (syn >= 1 && syn <= 32) {
                result = "(" + typeName + ", " + RESERVE_WORDS[syn - 1] + ")\n";
            } else if (syn >= 33 && syn <= OP_DELIMITERS.length + 32) {
                result = "(" + typeName + ", " + token + ")\n";
            } else if (syn == TOKEN_PREPROCESSOR) {
                result = "(" + typeName + ", " + token + ")\n";
            } else {
                result = "(" + typeName + ", " + token + ")\n";
            }

            System.out.print(result);
            outWriter.write(result);
        }

        outputIdentifierTable();
        outWriter.close();
        System.out.println("\n词法分析完成！结果已保存到 lex_out.txt");
    }

    // -------------------------- 主函数 --------------------------
    public static void main(String[] args) {
        try {
            String sourcePath = "/home/fancs/IdeaProjects/lex/src/test/testHard.c";
            String outputPath = "./out/lex_out.txt";

            Lex_Main analyzer = new Lex_Main(sourcePath, outputPath);
            analyzer.startAnalysis();
        } catch (FileNotFoundException e) {
            System.err.println("错误：源文件未找到！路径：" + e.getMessage());
        } catch (IOException e) {
            System.err.println("错误：文件读写异常：" + e.getMessage());
        }
    }
}